

#ifndef DELAYQUEUE_H
#define DELAYQUEUE_H

#include <cassert>
#include <cstdio>
#include <cstdlib>

#include "statwrapper.h"

template <class T>
struct FifoData
{
  T *m_data;
  FifoData *m_next;
};

template <class T>
class FifoPipeline
{
public:
  FifoPipeline(const char *nm, uint32_t minlen, uint32_t maxlen)
  {
    assert(maxlen);
    m_name = nm;
    m_min_len = minlen;
    m_max_len = maxlen;
    m_length = 0;
    m_n_element = 0;
    m_head = NULL;
    m_tail = NULL;
    for (uint32_t i = 0; i < m_min_len; i++)
      push(NULL);
  }

  ~FifoPipeline()
  {
    while (m_head)
    {
      m_tail = m_head;
      m_head = m_head->m_next;
      delete m_tail;
    }
  }

  void push(T *data)
  {
    assert(m_length < m_max_len);
    if (m_head)
    {
      if (m_tail->m_data || m_length < m_min_len)
      {
        m_tail->m_next = new FifoData<T>();
        m_tail = m_tail->m_next;
        m_length++;
        m_n_element++;
      }
    }
    else
    {
      m_head = m_tail = new FifoData<T>();
      m_length++;
      m_n_element++;
    }
    m_tail->m_next = NULL;
    m_tail->m_data = data;
  }

  T *pop()
  {
    FifoData<T> *next;
    T *data;
    if (m_head)
    {
      next = m_head->m_next;
      data = m_head->m_data;
      if (m_head == m_tail)
      {
        assert(next == NULL);
        m_tail = NULL;
      }
      delete m_head;
      m_head = next;
      m_length--;
      if (m_length == 0)
      {
        assert(m_head == NULL);
        m_tail = m_head;
      }
      m_n_element--;
      if (m_min_len && m_length < m_min_len)
      {
        push(NULL);
        m_n_element--; // uncount NULL elements inserted to create delays
      }
    }
    else
    {
      data = NULL;
    }
    return data;
  }

  T *top() const
  {
    if (m_head)
    {
      return m_head->m_data;
    }
    else
    {
      return NULL;
    }
  }

  void set_min_length(uint32_t new_min_len)
  {
    if (new_min_len == m_min_len)
      return;

    if (new_min_len > m_min_len)
    {
      m_min_len = new_min_len;
      while (m_length < m_min_len)
      {
        push(NULL);
        m_n_element--; // uncount NULL elements inserted to create delays
      }
    }
    else
    {
      // in this branch imply that the original min_len is larger then 0
      // ie. head != 0
      assert(m_head);
      m_min_len = new_min_len;
      while ((m_length > m_min_len) && (m_tail->m_data == 0))
      {
        FifoData<T> *iter;
        iter = m_head;
        while (iter && (iter->m_next != m_tail))
          iter = iter->m_next;
        if (!iter)
        {
          // there is only one node, and that node is empty
          assert(m_head->m_data == 0);
          pop();
        }
        else
        {
          // there are more than one node, and tail node is empty
          assert(iter->m_next == m_tail);
          delete m_tail;
          m_tail = iter;
          m_tail->m_next = 0;
          m_length--;
        }
      }
    }
  }

  bool full() const { return (m_max_len && m_length >= m_max_len); }
  bool is_avilable_size(uint32_t size) const
  {
    return (m_max_len && m_length + size - 1 >= m_max_len);
  }
  bool empty() const { return m_head == NULL; }
  uint32_t get_n_element() const { return m_n_element; }
  uint32_t get_length() const { return m_length; }
  uint32_t get_max_len() const { return m_max_len; }

  void print() const
  {
    FifoData<T> *ddp = m_head;
    printf("%s(%d): ", m_name, m_length);
    while (ddp)
    {
      printf("%p ", ddp->m_data);
      ddp = ddp->m_next;
    }
    printf("\n");
  }

private:
  const char *m_name;

  uint32_t m_min_len;
  uint32_t m_max_len;
  uint32_t m_length;
  uint32_t m_n_element;

  FifoData<T> *m_head;
  FifoData<T> *m_tail;
};

#endif
